package com.xiaoshuidi.cloud.module.member.service.user;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.annotations.VisibleForTesting;
import com.xiaoshuidi.cloud.framework.common.enums.CommonStatusEnum;
import com.xiaoshuidi.cloud.framework.common.exception.ServiceException;
import com.xiaoshuidi.cloud.framework.common.exception.enums.GlobalErrorCodeConstants;
import com.xiaoshuidi.cloud.framework.common.pojo.CommonResult;
import com.xiaoshuidi.cloud.framework.common.pojo.PageResult;
import com.xiaoshuidi.cloud.framework.common.util.json.JsonUtils;
import com.xiaoshuidi.cloud.framework.redis.lock.DistributeLock;
import com.xiaoshuidi.cloud.framework.security.core.util.SecurityFrameworkUtils;
import com.xiaoshuidi.cloud.framework.systemevent.core.data.LeaseSystemEventMobileModify;
import com.xiaoshuidi.cloud.module.infra.api.file.FileApi;
import com.xiaoshuidi.cloud.module.member.api.user.dto.MemberUserAuthInfoDTO;
import com.xiaoshuidi.cloud.module.member.api.user.dto.MemberUserPageDTO;
import com.xiaoshuidi.cloud.module.member.api.user.dto.MemberUserReqDTO;
import com.xiaoshuidi.cloud.module.member.api.user.dto.MemberUserRespDTO;
import com.xiaoshuidi.cloud.module.member.api.mobilemodifyrecord.vo.MobileModifyRecordCreateReqVO;
import com.xiaoshuidi.cloud.module.member.api.user.dto.ThirdPayUserReqDTO;
import com.xiaoshuidi.cloud.module.member.controller.app.invite.vo.AddInviteLogVo;
import com.xiaoshuidi.cloud.module.member.controller.app.point.vo.TenantPointsVo;
import com.xiaoshuidi.cloud.module.member.controller.app.point.vo.UserPointInfo;
import com.xiaoshuidi.cloud.module.member.controller.app.user.vo.*;
import com.xiaoshuidi.cloud.module.member.convert.user.UserConvert;
import com.xiaoshuidi.cloud.module.member.enums.ErrorCodeConstants;
import com.xiaoshuidi.cloud.module.member.enums.MemberUserStaticsticsEnum;
import com.xiaoshuidi.cloud.module.member.enums.UserStateEnum;
import com.xiaoshuidi.cloud.module.member.mapper.MemberUserMapper;
import com.xiaoshuidi.cloud.module.member.mq.producer.MemberUpdateMoibleProducer;
import com.xiaoshuidi.cloud.module.member.pojo.user.MemberUser;
import com.xiaoshuidi.cloud.module.member.repository.MemberUserRepository;
import com.xiaoshuidi.cloud.module.member.service.invite.InviteActivityLogService;
import com.xiaoshuidi.cloud.module.member.service.mobilemodifyrecord.MemberMobileModifyRecordService;
import com.xiaoshuidi.cloud.module.member.service.point.MemberPointsService;
import com.xiaoshuidi.cloud.module.system.api.sms.SmsCodeApi;
import com.xiaoshuidi.cloud.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
import com.xiaoshuidi.cloud.module.system.enums.sms.SmsSceneEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import static com.xiaoshuidi.cloud.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.xiaoshuidi.cloud.framework.common.util.servlet.ServletUtils.getClientIP;
import static com.xiaoshuidi.cloud.module.member.enums.ErrorCodeConstants.*;

/**
 * 会员 User Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Valid
@Slf4j
public class MemberUserServiceImpl extends ServiceImpl<MemberUserMapper, MemberUser> implements MemberUserService {
    @Resource
    private MemberUserMapper memberUserMapper;


    @Resource
    private MemberUserRepository memberUserRepository;

    @Resource
    private FileApi fileApi;
    @Resource
    private SmsCodeApi smsCodeApi;

    @Resource
    private PasswordEncoder passwordEncoder;
    @Resource
    private InviteActivityLogService inviteActivityLogService;

    @Resource
    private MemberPointsService tenantPointsService;

    @Resource
    private MemberUpdateMoibleProducer memberUpdateMoibleProducer;

    @Resource
    private MemberAuthenticationService memberAuthenticationService;
    @Resource
    private MemberMobileModifyRecordService memberMobileModifyRecordService;

    public static final String ADD_USER = "user:add:";//新增用户

    @Override
    public MemberUser getUserByMobile(String mobile) {
        return memberUserMapper.selectByMobile(mobile);
    }

    @Override
    public List<MemberUser> getUserListByNickname(String nickname) {
        return memberUserMapper.selectListByNicknameLike(nickname);
    }

    @Override
    @Transactional
    public MemberUser createUserIfAbsent(String mobile, Long recommendId) {
        // 用户已经存在
        MemberUser user = memberUserMapper.selectByMobile(mobile);
        if (user != null) {
            return user;
        }
        // 用户不存在，则进行创建
        user = this.createUser(mobile, recommendId);
        // 如果是邀请新客,需要发放积分
        try {
            if (recommendId != null) {
                AddInviteLogVo logVo = new AddInviteLogVo();
                logVo.setAccountId(recommendId);
                logVo.setInviteAccountId(user.getId());
                logVo.setInvitePhone(user.getMobile());
                inviteActivityLogService.addUserInviteLog(logVo);
                log.info("发放邀请积分成功");
            }
        } catch (Exception e) {
            log.error("发放邀请积分失败", e);
        }
        return user;
    }

    private MemberUser createUser(String mobile, Long recommendId) {
        String key = ADD_USER.concat(mobile);
        // 生成密码
        String password = IdUtil.fastSimpleUUID();
        try (DistributeLock lock = DistributeLock.getLock(key)) {
            if (lock.tryLock()) {
                // 插入用户
                MemberUser user = new MemberUser();
                user.setMobile(mobile);
                user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启
                user.setPassword(encodePassword(password)); // 加密密码
                user.setStatus(UserStateEnum.INUSE.getCode());
                user.setAuthenticated(false);
                user.setNickname(mobile + "用户");
                if (ObjectUtils.isNotEmpty(recommendId)) {
                    user.setRecommendId(recommendId);
                }
                memberUserMapper.insert(user);
                return user;
            }
        }
        return null;
    }

    @Override
    public void updateUserLogin(Long id, String loginIp) {
        memberUserMapper.updateById(new MemberUser().setId(id)
                //.setLoginIp(loginIp)
                .setLoginDate(LocalDateTime.now()));
    }

    @Override
    public MemberUser getUser(Long id) {
        return memberUserMapper.selectById(id);
    }

    @Override
    public List<MemberUser> getUserList(Collection<Long> ids) {
        return memberUserMapper.selectBatchIds(ids);
    }

    @Override
    public void updateUserNickname(Long userId, String nickname) {
        MemberUser user = this.checkUserExists(userId);
        // 仅当新昵称不等于旧昵称时进行修改
        if (nickname.equals(user.getNickname())) {
            return;
        }
        MemberUser userDO = new MemberUser();
        userDO.setId(user.getId());
        userDO.setNickname(nickname);
        memberUserMapper.updateById(userDO);
    }

    @Override
    public String updateUserAvatar(Long userId, InputStream avatarFile) throws Exception {
        this.checkUserExists(userId);
        // 创建文件
        String avatar = fileApi.createFile(IoUtil.readBytes(avatarFile));
        // 更新头像路径
        memberUserMapper.updateById(MemberUser.builder().id(userId).avatar(avatar).build());
        return avatar;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateUserMobile(Long userId, AppUserUpdateMobileReqVO reqVO) {
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        if (loginUserId.intValue() != userId.intValue()) {
            throw exception(USER_UPDATE_MOBILE_VALID);
        }
        // TODO 芋艿：oldMobile 应该不用传递
        //检验用户新的手机号是否存在
        if (reqVO.getOldMobile().equals(reqVO.getMobile())) {
            throw exception(USER_UPDATE_MOBILE_ERROR);
        } else {
            MemberUser u = this.baseMapper.getUserByMobile(reqVO.getMobile());
            if (ObjectUtils.isNotEmpty(u)) {
                throw exception(MOBILE_EXIST);
            }

        }
        //查询新旧手机
        MemberUser old = this.baseMapper.getUserByMobile(reqVO.getOldMobile());
        if (ObjectUtils.isEmpty(old)) {
            throw exception(USER_OLDMOBILE_NOT_EXIST);
        }
        // 校验旧手机和旧验证码
        SmsCodeUseReqDTO oldSmsCodeUseReqDTO = new SmsCodeUseReqDTO();
        oldSmsCodeUseReqDTO.setMobile(reqVO.getOldMobile());
        oldSmsCodeUseReqDTO.setCode(reqVO.getOldCode());
        oldSmsCodeUseReqDTO.setScene(SmsSceneEnum.MEMBER_FORGET_PASSWORD.getScene());
        oldSmsCodeUseReqDTO.setUsedIp(getClientIP());
        CommonResult<Boolean> commonResult = smsCodeApi.useSmsCode(oldSmsCodeUseReqDTO);
        if (!commonResult.isSuccess()) {
            throw new ServiceException(ErrorCodeConstants.CODE_FILED);
        }

        SmsCodeUseReqDTO smsCodeUseReqDTO = new SmsCodeUseReqDTO();
        smsCodeUseReqDTO.setMobile(reqVO.getMobile());
        smsCodeUseReqDTO.setCode(reqVO.getCode());
        smsCodeUseReqDTO.setScene(SmsSceneEnum.MEMBER_FORGET_PASSWORD.getScene());
        smsCodeUseReqDTO.setUsedIp(getClientIP());
        commonResult = smsCodeApi.useSmsCode(smsCodeUseReqDTO);
        if (!commonResult.isSuccess()) {
            throw new ServiceException(ErrorCodeConstants.CODE_FILED);
        }

        try {
            if (old.getAuthenticated()) {

                memberUserMapper.updateById(MemberUser.builder().id(old.getId()).mobile(reqVO.getMobile())
                        .disableDesc("原手机号为" + reqVO.getOldMobile() + ",修改手机号为" + reqVO.getMobile() + "")
                        .authenticated(false).build());
                //删除实名认证记录
                memberAuthenticationService.deleteUser(old.getId());
            } else {
                // 更新用户手机
                memberUserMapper.updateById(MemberUser.builder().id(old.getId()).mobile(reqVO.getMobile())
                        .disableDesc("原手机号为" + reqVO.getOldMobile() + ",修改手机号为" + reqVO.getMobile() + "").build());

            }
        } catch (Exception e) {
            log.error("修改手机号出现异常,{}", e.getMessage());
        }
        // 记录手机号修改
        memberMobileModifyRecordService.createRecord(new MobileModifyRecordCreateReqVO().setUserId(old.getId())
                .setUserName(old.getNickname())
                .setRealName(old.getName())
                .setCardNo(old.getCardNo())
                .setBeforeMobile(old.getMobile())
                .setAfterMobile(reqVO.getMobile())
                .setUserCreateTime(old.getCreateTime())
                .setOperator(SecurityFrameworkUtils.getLoginUser().getNickname())
        );
        //发消息
        LeaseSystemEventMobileModify mobileModify = new LeaseSystemEventMobileModify();
        mobileModify.setOldUserId(old.getId());
        mobileModify.setOldMobile(reqVO.getOldMobile());
        mobileModify.setUserId(old.getId());
        mobileModify.setMobile(reqVO.getMobile());
        log.info("发送的修改手机消息参数为{}", JSONObject.toJSONString(mobileModify));
        memberUpdateMoibleProducer.sendUpdateMobile(mobileModify);

    }

    @Override
    public boolean isPasswordMatch(String rawPassword, String encodedPassword) {
        return passwordEncoder.matches(rawPassword, encodedPassword);
    }

    @Override
    public boolean createUser(AppUserInfoCreateVO vo) {
        String key = ADD_USER.concat(vo.getMobile());
        try (DistributeLock lock = DistributeLock.getLock(key)) {
            if (lock.tryLock()) {
                //判断用户是否存在
                MemberUser user = memberUserMapper.selectByMobile(vo.getMobile());
                if (user != null) {
                    throw new ServiceException(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode(), "此用户已注册!");
                }
                MemberUser memberUserDO = new MemberUser();
                BeanUtils.copyProperties(vo, memberUserDO);
                memberUserDO.setStatus(UserStateEnum.INUSE.getCode());
                memberUserDO.setAuthenticated(false);
                if (StringUtils.isEmpty(vo.getNickname())) {
                    memberUserDO.setNickname(vo.getMobile() + "用户");
                }
                //紧急联系人信息
                if (vo.getEmergencyContacts() != null && vo.getEmergencyContacts().size() > 0) {
                    memberUserDO.setEmergencyContact(JsonUtils.toJsonString(vo.getEmergencyContacts()));
                }

                memberUserDO.setLoginDate(LocalDateTime.now());
                memberUserMapper.insert(memberUserDO);
                return true;
            }
        }
        return true;
    }

    @Override
    public List<MemberUserStatisticsVO> getMemberStatistics() {
        List<MemberUser> list = memberUserRepository.list();
        //统计出各种情况的数量
        List<MemberUserStatisticsVO> result = new ArrayList<>();
        for (MemberUserStaticsticsEnum value : MemberUserStaticsticsEnum.values()) {
            MemberUserStatisticsVO vo = new MemberUserStatisticsVO();
            vo.setCode(value.getCode());
            vo.setName(value.getName());
            if (vo.getCode().equals(MemberUserStaticsticsEnum.TOTAL.getCode())) {
                vo.setNum((long) list.size());
            } else if (vo.getCode().equals(MemberUserStaticsticsEnum.AUTHENTICATED.getCode())) {
                long count = list.stream().filter(m -> m.getAuthenticated()).count();
                vo.setNum(count);
            } else if (vo.getCode().equals(MemberUserStaticsticsEnum.UNAUTHENTICATED.getCode())) {
                long count = list.stream().filter(m -> !m.getAuthenticated()).count();
                vo.setNum(count);
            } else if (vo.getCode().equals(MemberUserStaticsticsEnum.TODAYNEWUSER.getCode())) {
                LocalDateTime beginOfDay = LocalDateTimeUtil.beginOfDay(LocalDateTime.now());
                LocalDateTime endOfDay = LocalDateTimeUtil.endOfDay(LocalDateTime.now());
                long count = list.stream().filter(m -> m.getCreateTime().isAfter(beginOfDay) && m.getCreateTime().isBefore(endOfDay)).count();
                vo.setNum(count);
            } else if (vo.getCode().equals(MemberUserStaticsticsEnum.WEEKNEWUSER.getCode())) {
                LocalDateTime now = LocalDateTime.now();
                int dayOfWeek = now.getDayOfWeek().getValue();
                LocalDateTime weekStart = LocalDateTimeUtil.beginOfDay(now.minusDays(dayOfWeek - 1));
                LocalDateTime weekEnd = LocalDateTimeUtil.endOfDay(now.plusDays(7 - dayOfWeek));
                long count = list.stream().filter(m -> m.getCreateTime().isAfter(weekStart) && m.getCreateTime().isBefore(weekEnd)).count();
                vo.setNum(count);
            }
            result.add(vo);
        }

        return result;
    }


    @Override
    public PageResult<MemberVO> getMemberPage(MemberPageReqVO memberPageReqVo) {
        PageResult<MemberVO> page = memberUserRepository.getMemberPage(memberPageReqVo);
        return page;
    }

    @Override
    public MemberUserRespDTO getUserInfo(MemberUserReqDTO req) {
        return memberUserRepository.getUserInfo(req);
    }

    @Override
    public AppUserInfoRespVO getUserInfoById(Long id) {
        AppUserInfoRespVO vo = memberUserRepository.getUserInfoById(id);
        //设置积分
        try {
            TenantPointsVo pointsVo = tenantPointsService.getByTenantUserId(id);
            vo.setScore(pointsVo.getAvailablePoints().intValue());
            vo.setLevel(pointsVo.getRank().toString());
            vo.setPointId(pointsVo.getId());
        } catch (Exception e) {
            log.error("获取个人积分失败");
        }

        return vo;
    }

    @Override
    public Boolean memberUpdateState(MemberUpdateStateVO memberUpdateStateVO) {
        return memberUserRepository.memberUpdateState(memberUpdateStateVO);
    }

    @Override
    public MemberUserAuthInfoDTO getUserAuthInfo(String moible) {
        return memberUserRepository.getUserAuthInfo(moible);
    }

    @Override
    public PageResult<MemberUserRespDTO> getUserPage(MemberUserPageDTO page) {
        PageResult<MemberUserRespDTO> result = memberUserRepository.getUserPage(page);
        return result;
    }

    @Override
    public Boolean editUser(AppUserInfoCreateVO vo) {
        if (StringUtils.isEmpty(vo.getName())) {
            throw exception(USER_NAME_FAILED);
        }
        //查询修改的用户id,是否和登录的id一致
        Long id = SecurityFrameworkUtils.getLoginUser().getId();
        if (id.intValue() != vo.getId().intValue()) {
            throw exception(USER_UPDATE_MOBILE_VALID);
        }
        //查询这个用户以前的手机号
        MemberUser user = memberUserMapper.selectById(vo.getId());
        if (user == null) {
            throw exception(USER_NOT_EXISTS);
        }
        if (!user.getMobile().equals(vo.getMobile())) {
            //表示此操作修改了手机号，则需要修改后的手机号是不是已经注册过了，如果注册过来，则抛异常，
            MemberUser u = memberUserRepository.getUserByMobile(vo.getMobile());
            if (ObjectUtils.isNotEmpty(u)) {
                throw exception(MOBILE_EXIST);
            }
        }
        MemberUser memberUserDO = new MemberUser();
        BeanUtils.copyProperties(vo, memberUserDO);
        //紧急联系人信息
        if (vo.getEmergencyContacts() != null && vo.getEmergencyContacts().size() > 0) {
            memberUserDO.setEmergencyContact(JsonUtils.toJsonString(vo.getEmergencyContacts()));
        }
        memberUserDO.setLoginDate(LocalDateTime.now());
        memberUserMapper.updateById(memberUserDO);
        return true;
    }

    @Override
    public List<MemberUserRespDTO> getUsersByIds(List<Long> ids) {
        return memberUserRepository.getUsersByIds(ids);
    }

    @Override
    public MemberUserRespDTO getUserById(Long id) {
        return memberUserRepository.getUsersById(id);
    }

    @Override
    public List<MemberUser> getBirthDayUser() {
        return memberUserRepository.getBirthDayUser();
    }

    @Override
    public UserPointInfo getUserPointInfo(Long id) {
        AppUserInfoRespVO userInfoById = getUserInfoById(id);
        UserPointInfo userPointInfo = new UserPointInfo();
        BeanUtils.copyProperties(userInfoById, userPointInfo);
        if (ObjectUtils.isNotEmpty(userInfoById.getBirthday())) {
            userPointInfo.setBirthday(LocalDateTimeUtil.format(userInfoById.getBirthday(), "yyyy-MM-dd"));
        }
        return userPointInfo;
    }

    @Override
    public Boolean registerThirdMember(ThirdPayUserReqDTO thirdPayUserReqDTO) {
        MemberUser user = new MemberUser().setId(thirdPayUserReqDTO.getMemberId())
                .setThirdMemberId(thirdPayUserReqDTO.getThirdMemberId());
        baseMapper.updateById(user);
        return true;
    }

    @Override
    public Boolean bindPayFlag(Long id) {
        MemberUser user = new MemberUser().setId(id)
                .setBindFlag(true);
        baseMapper.updateById(user);
        return true;
    }


    /**
     * 根据生日月份获取用户列表
     * @param month 生日月份，ex: 01  02  12
     * @return
     */
    @Override
    public List<MemberUser> getUserByBirthdayMonth(String month) {
        return memberUserMapper.getUserByBirthdayMonth(month);
    }

    @Override
    public MemberUserRespDTO getUserByCardNo(String cardNo) {
        LambdaQueryWrapper<MemberUser> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(MemberUser::getCardNo, cardNo);
        wrapper.orderByDesc(MemberUser::getCreateTime);
        wrapper.last("limit 1");
        MemberUser memberUser = memberUserMapper.selectOne(wrapper);
        return UserConvert.INSTANCE.convert2(memberUser);
    }

    /**
     * 对密码进行加密
     *
     * @param password 密码
     * @return 加密后的密码
     */
    private String encodePassword(String password) {
        return passwordEncoder.encode(password);
    }

    @VisibleForTesting
    public MemberUser checkUserExists(Long id) {
        if (id == null) {
            return null;
        }
        MemberUser user = memberUserMapper.selectById(id);
        if (user == null) {
            throw exception(USER_NOT_EXISTS);
        }
        return user;
    }

}
